package implementation1.app.streams;

import exceptions.StreamEOFException;
import exceptions.StreamException;
import interfaces.app.IStream;

import java.util.ArrayList;
import java.util.ResourceBundle;
import java.util.concurrent.Semaphore;

/**
 * Created: 1.12.11
 *
 * @author Martin Štulc (<a href="mailto:martin.stulc@gmail.com">martin.stulc@gmail.com</a>)
 * @version 1.0
 */
public class SendStream implements IStream
{
    /**
     * TRUE - stream is exclusive, FALSE - stream isn't exclusive
     */
    private boolean isExclusive;

    /**
     * Resource of messages.
     */
    private ResourceBundle bundle = ResourceBundle.getBundle("implementation1.res.messages");

    /**
     * Used like FIFO queue. Into this list are saved lines from input. This lines are removed during reading the stream.
     */
    private ArrayList<String> buffer;

    /**
     * Semaphore for readable strings.
     */
    private Semaphore readable;

    /**
     * Singleton var of instance.
     */
    private static SendStream singleton;

    /**
     * Return the singleton instance.
     * @return
     */
    public static IStream getInstance(){
        if (singleton == null)
        {
            singleton = new SendStream();
        }
        return singleton;
    }

    /**
     * private constructor.
     */
    private SendStream()
    {
        isExclusive = false;
        buffer = new ArrayList<String>();

        readable = new Semaphore(0);
    }

    /**
     * Return true if the stream is opened.
     *
     * @return true if the stream is opened; otherwise false
     */
    @Override
    public boolean isOpen()
    {
        return true;
    }

    /**
     * Return true if the stream is opened exclusively.
     *
     * @return true if the stream is opened exclusively; otherwise false
     */
    @Override
    public boolean isExclusive()
    {
        return false;
    }

    /**
     * Open the stream, if no possible return false.
     *
     * @param accessAttribute access to the stream; 1=read; 2=write
     * @return true if opening was successful; otherwise false
     */
    @Override
    public boolean tryOpen(int accessAttribute)
    {
        return true;
    }

    /**
     * Open the stream, if no possible then wait.
     *
     * @param accessAttribute access to the stream; 1=read; 2=write
     * @throws StreamException Thrown if occurs error during opening of the stream.
     */
    @Override
    public void open(int accessAttribute) throws StreamException
    {
        if (accessAttribute != AccessAttributes.READ && accessAttribute != AccessAttributes.WRITE)
        {
            throw new StreamException(bundle.getString("error.stream.openOnlyForReadOrWrite"));
        }


    }

    /**
     * Close the stream.
     *
     * @throws StreamException if stream can not be closed
     */
    @Override
    public void close() throws StreamException
    {

    }

    /**
     * Read a line from the stream if can not read wait.
     *
     * @return the line from stream
     * @throws StreamException    Thrown if occurs error during reading.
     * @throws exceptions.StreamEOFException Thrown if occurs EOF on input.
     */
    @Override
    public String readLine() throws StreamException, StreamEOFException
    {
        try
        {
            readable.acquire();
        } catch (InterruptedException e)
        {
            throw new StreamEOFException();
        }

        String line = buffer.remove(0);
        if (line == null)
        {
            throw new StreamEOFException();
        }
        return line;
    }

    /**
     * Write a line to the stream.
     *
     * @param line a line
     * @throws StreamException Thrown if occurs error during writing into stream.
     */
    @Override
    synchronized public void writeLine(String line) throws StreamException
    {
        buffer.add(line);
        readable.release();
    }

    /**
     * Notify all waiting process.
     */
    @Override
    public void notifyAllProcess()
    {
        synchronized (this)
        {
            this.notifyAll();
        }
    }

    @Override
    public void setExclusive(boolean exclusive)
    {
        isExclusive = exclusive;
    }
}
